package zhuzx.generator;

import cn.hutool.core.util.ClassUtil;
import com.alibaba.fastjson.JSON;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import lombok.extern.slf4j.Slf4j;
import org.zhuzx.util.GeneralUtil;

import java.io.StringWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * 模板统一放在resources下的templates目录中
 * @author zhuzx 2022/9/29
 */
@Slf4j
public class FreemarkerUtil {
	
	private static final Configuration cfg = initConfiguration();
	private static Configuration initConfiguration() {
		Configuration cfg = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
		cfg.setClassForTemplateLoading(FreemarkerUtil.class, "/templates/");
		cfg.setDefaultEncoding("UTF-8");
		cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
		return cfg;
	}

	/**
	 * 根据模板名和数据生成文本
	 * @param templateName ftl模板文件名
	 * @param data 需要在模板中使用的数据
	 */
	public static String generate(String templateName, Map<String, Object> data) {
		Template temp;
		try {
			temp = cfg.getTemplate(templateName);
			StringWriter writer = new StringWriter();
			Map<String,Object> copy = new HashMap<>(data);
			copy.putAll(constructAllUtil());
			temp.process(copy, writer);
			return writer.toString();
		} catch (Exception e) {
			throw new IllegalArgumentException(e);
		}
	}

	/**
	 * Freemarker无法直接从实例化对象中取属性，只能调方法，直接通过类名.属性名会报错InvalidReferenceException。<br>
	 * 暂时使用两级Map的形式装载静态常量，以constants键的Map下面再设立每个类名为键的子Map，
	 * 统一通过constants.类名.属性名引用。<br>
	 * @return 返回各个类名为键，类实例为值的Map，另额外返回一个constants的静态常量集。
	 */
	public static Map<String,Object> constructAllUtil() {
		Map<String,Object> data = new LinkedHashMap<>();
		Set<Class<?>> utils = ClassUtil.scanPackage("org.zhuzx.util",
				aClass -> aClass.getSimpleName().endsWith("Util") || aClass.getSimpleName().endsWith("s"));
		LinkedHashMap<String, Object> constMap = new LinkedHashMap<>();
		data.put("constants", constMap);
		utils.forEach(cla -> {
			if (cla.isInterface() || cla.isEnum() || Modifier.isAbstract(cla.getModifiers())) {
				return;
			}
			try {
				Constructor<?> constructor = cla.getConstructors()[0];
				constructor.setAccessible(true);
				Object value = constructor.newInstance();
				data.put(cla.getSimpleName(), value);
				Field[] fields = cla.getFields();
				if (!GeneralUtil.isEmpty(fields)) {
					Map<String,Object> subConstMap = new LinkedHashMap<>();
					constMap.put(cla.getSimpleName(), subConstMap);
					Arrays.stream(fields).forEach(field -> {
						try {
							subConstMap.put(field.getName(), field.get(value));
						} catch (IllegalAccessException e) {
							throw new RuntimeException(e);
						}
					});
				}
			} catch (Throwable e) {
				throw new RuntimeException(e);
			}
		});
		log.debug("工具类Map已初始化：\n{}", JSON.toJSONString(data));
		return data;
	}

}